package com.yixuan.mt.console.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;

@Configuration
public class DynamicRegisterBeanConfig {

    public Object getValueFromMap(Map<String, Object> map, String key) {
        String[] keys = key.split("\\.");
        Map<String, Object> subMap = map;
        for (int i = 0; i < keys.length - 1; i++) {
            if (!subMap.containsKey(keys[i])) {
                return null;
            }
            subMap = (Map<String, Object>) subMap.get(keys[i]);
        }
        return subMap.get(keys[keys.length - 1]);
    }

    @Bean
    public BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {
        Yaml yaml = new Yaml();
        InputStream inputStream = DynamicRegisterBeanConfig.class.getClassLoader().getResourceAsStream("application.yml");

        if (inputStream == null) {
            throw new RuntimeException("不能找到 application.yml");
        }

        Map<String, Object> map = yaml.load(inputStream);

        String metadataMode = (String) getValueFromMap(map, "mt.metadata.mode");
        if (metadataMode == null) {
            metadataMode = "local";
        }

        String finalMetadataMode = metadataMode;
        return registry -> {
            ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
            provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
            Set<BeanDefinition> components;
            if (finalMetadataMode.equals("mysql")) {
                // JdbcConfig
                String driver = "com.mysql.cj.jdbc.Driver";
                String url = (String) getValueFromMap(map, "jdbc.url");
                String username = (String) getValueFromMap(map, "jdbc.username");
                String password = (String) getValueFromMap(map, "jdbc.password");
                BeanDefinitionBuilder druidDataSourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
                druidDataSourceBuilder.addPropertyValue("driverClassName", driver);
                druidDataSourceBuilder.addPropertyValue("url", url);
                druidDataSourceBuilder.addPropertyValue("username", username);
                druidDataSourceBuilder.addPropertyValue("password", password);
                druidDataSourceBuilder.addPropertyValue("failFast", true);
                druidDataSourceBuilder.addPropertyValue("timeBetweenConnectErrorMillis", 5000);
//                druidDataSourceBuilder.addPropertyValue("breakAfterAcquireFailure", true);
//                druidDataSourceBuilder.addPropertyValue("connectionErrorRetryAttempts", 3);
                registry.registerBeanDefinition("dataSource", druidDataSourceBuilder.getBeanDefinition());

                // MyBatisConfig
                BeanDefinitionBuilder sqlSessionFactoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(SqlSessionFactoryBean.class);
                sqlSessionFactoryBeanBuilder.addPropertyReference("dataSource", "dataSource");
                org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
                configuration.setMapUnderscoreToCamelCase(true);
                sqlSessionFactoryBeanBuilder.addPropertyValue("configuration", configuration);
                try {
                    sqlSessionFactoryBeanBuilder.addPropertyValue("mapperLocations", new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
                } catch (IOException e) {
                }
                registry.registerBeanDefinition("sqlSessionFactoryBean", sqlSessionFactoryBeanBuilder.getBeanDefinition());

                BeanDefinitionBuilder transactionManagerBuilder = BeanDefinitionBuilder.genericBeanDefinition(DataSourceTransactionManager.class);
                transactionManagerBuilder.addConstructorArgReference("dataSource");
                registry.registerBeanDefinition("transactionManager", transactionManagerBuilder.getBeanDefinition());

                BeanDefinitionBuilder mapperScannerConfigurerBuilder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
                mapperScannerConfigurerBuilder.addPropertyValue("basePackage", "com.yixuan.mt.common.mapper");
                registry.registerBeanDefinition("mapperScannerConfigurer", mapperScannerConfigurerBuilder.getBeanDefinition());

                components = provider.findCandidateComponents("com.yixuan.mt.common.service.impl_mysql");
            } else {
                components = provider.findCandidateComponents("com.yixuan.mt.common.service.impl");
            }
            for (BeanDefinition component : components) {
                registry.registerBeanDefinition(component.getBeanClassName(), component);
            }
        };
    }
}
